package com.cqupt.scanning.security.filter;

import cn.hutool.core.util.StrUtil;
import com.cqupt.scanning.security.config.JwtSecurityProperties;
import com.cqupt.scanning.security.utils.JwtTokenUtil;
import com.cqupt.scanning.security.utils.ResponseUtil;
import com.cqupt.scanning.system.utils.result.R;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.InvalidClaimException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @Data: 2021/7/12 10:29
 * @Author: 宋宝梁
 */
@Slf4j
public class TokenAuthenticationFilter extends BasicAuthenticationFilter {
    private JwtTokenUtil jwtTokenUtil;
    private RedisTemplate<String, Object> redisTemplate;
    private JwtSecurityProperties jwtSecurityProperties;

    public TokenAuthenticationFilter(AuthenticationManager authManager,
                                     JwtTokenUtil jwtTokenUtil,
                                     RedisTemplate<String, Object> redisTemplate,
                                     JwtSecurityProperties jwtSecurityProperties) {
        super(authManager);
        this.jwtTokenUtil = jwtTokenUtil;
        this.redisTemplate = redisTemplate;
        this.jwtSecurityProperties = jwtSecurityProperties;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        UsernamePasswordAuthenticationToken authentication = null;
        try {
            authentication = getAuthentication(req);
        } catch (ExpiredJwtException e) {
            ResponseUtil.out(res, R.error().code(30001).message("Token失效"));
            return;
        } catch (InvalidClaimException e) {
            ResponseUtil.out(res, R.error().code(30002).message("Token非法"));
            return;
        } catch (Exception e) {
            ResponseUtil.out(res, R.error().code(30003).message("认证失败"));
            return;
        }

        if (authentication != null) {
            SecurityContextHolder.getContext().setAuthentication(authentication);
        } else {
            ResponseUtil.out(res, R.error().code(30004).message("请求头里没有token"));
            return;
        }
        log.info("访问路径[{}]",  req.getRequestURI());
        chain.doFilter(req, res);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String authHeader = request.getHeader(jwtSecurityProperties.getHeader());
        if (authHeader != null && authHeader.startsWith(jwtSecurityProperties.getTokenStartWith())) {
            final String token = authHeader.substring(jwtSecurityProperties.getTokenStartWith().length());
            String userName = jwtTokenUtil.parseToken(token);
            List<String> permissionValueList = (List<String>) redisTemplate.opsForValue().get(userName);
            Collection<GrantedAuthority> authorities = new ArrayList<>();
            for(String permissionValue : permissionValueList) {
                if(StrUtil.hasEmpty(permissionValue)) continue;
                SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
                authorities.add(authority);
            }

            if (!StrUtil.hasEmpty(userName)) {
                // 存入authorities用于接口授权
                return new UsernamePasswordAuthenticationToken(userName, token, authorities);
            }
            return null;
        }
        return null;
    }
}
